import json
import urlparse
from django.contrib.auth.models import User, Group, Permission, ContentType
from rest_framework import generics
from rest_framework import status
from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.decorators import api_view
from rest_framework.reverse import reverse
from rest_framework.response import Response
from munkiwebadmin.reports.models import Machine, MunkiReport
from munkiwebadmin.inventory.models import InventoryItem
from munkiwebadmin.catalogs.models import Catalog
from munkiwebadmin.manifests.models import Manifest
from api.serializers import UserSerializer, GroupSerializer, PermissionSerializer, ContentTypeSerializer, MachineSerializer, ReportSerializer, InventoryItemSerializer

@api_view(['GET'])
def api_root(r, f=None):
    """
    The entry point of our API.
    """
    return Response({
        'users': reverse('user-list', request=r, format=f),
        'groups': reverse('group-list', request=r, format=f),
        'machines': reverse('machine-list', request=r, format=f),
        'reports': reverse('report-list', request=r, format=f),
        'inventoryitems': reverse('inventoryitem-list', request=r, format=f),
        'catalogs': reverse('catalog-list', request=r, format=f),
        'manifests': reverse('manifest-list', request=r, format=f),
        'permissions': reverse('permission-list', request=r, format=f),
        'contenttypes': reverse('contenttype-list', request=r, format=f),
    })





def parseQueryStringFromRequest(request):
    return urlparse.parse_qs(request.body)




def getJSONFromRequest(request):
    newDict = dict()
    try:
        newDict = json.loads('%s' % request.body)
    except:
        try:
            tmpDict = urlparse.parse_qs(request.body)
            newDict = json.loads('%s' % urlparse.parse_qs(request.body)['_content'][0])
        except:
            return False
    return newDict


def verifyPermission(request, perm):
    if not request.user.has_perm(perm):
        return Response({
            'detail': 'Action denied for current user. Requested permission "%s"' % perm
        }, status.HTTP_403_FORBIDDEN)
    return None
    



class CatalogList(APIView):
    """
    API endpoint representing a list of catalogs.
    """
    def get(self, request, *args, **kw):
        catalog = Catalog()
        catalogResults = []
        for cat in catalog.list():
             catalogResults += [{
                'url': reverse('catalog-detail', args=[cat], request=request),
                'catalog_name': cat
            }]
        return Response(catalogResults)



class CatalogDetail(APIView):
    """
    API endpoint representing a single catalog.
    """
    def get(self, request, *args, **kw):
        cat = kw['catalog_name']
        response = Response(Catalog.detail(cat))
        return response



class ManifestList(APIView):
    """
    API endpoint representing a list of manifests.
    """
    permissions_classes = (permissions.IsAuthenticated,)
    class Meta:
        model = Manifest
    def get(self, request, *args, **kw):
        manifest = Manifest()
        manifestResults = []
        for manifestName in manifest.list():
            manifestResults += [{
                'url': reverse('manifest-detail', args=[manifestName], request=request),
                'manifest_name': manifestName
            }]
        return Response(manifestResults)



    def post(self, request, *args, **kw):
        denied = verifyPermission(request, 'reports.add_machine')
        if denied:
            return denied

        # read the json object from the post body, get the deisred manifest
        # name, then pop that manifest_name out of the json object so it's
        # not written to the actual manifest file.
        jsonObj = getJSONFromRequest(request)
        if jsonObj == False:
            return Response({
                'detail': "Unable to parse JSON payload.",
            }, status.HTTP_500_INTERNAL_SERVER_ERROR)
        manifestName = jsonObj['manifest_name']
        jsonObj.pop("manifest_name", None)

        # Make sure the new manifest name doesn't already exist.
        if manifestName in Manifest.list():
            return Response({
                'detail': 'A manifest with that name already exists'
            }, status.HTTP_409_CONFLICT)
        
        newManifest = Manifest.new()
        newManifest.update(jsonObj)
        Manifest.write(manifestName, newManifest, request.user)
        return Response(newManifest, status.HTTP_201_CREATED)



class ManifestDetail(APIView):
    """
    API endpoint representing a single manifest.
    """

    def recursivelyMapIncludedManifests(self, request, manifest):
        mappedManifests = []
        if 'included_manifests' in manifest:
            for man in manifest['included_manifests']:
                mappedManifests += [reverse('manifest-detail', args=[man], request=request)]
            manifest['included_manifests'] = mappedManifests
        if 'conditional_items' in manifest:
            mappedConditionalItems = []
            for cond in manifest['conditional_items']:
                mappedConditionalItems += [self.recursivelyMapIncludedManifests(request=request, manifest=cond)]
            manifest['conditional_items'] = mappedConditionalItems
        return manifest
    


    def recursivelyMapCatalogs(self, request, manifest):
        mappedCatalogs = []
        if 'catalogs' in manifest:
            for cat in manifest['catalogs']:
                mappedCatalogs += [reverse('catalog-detail', args=[cat], request=request)]
            manifest['catalogs'] = mappedCatalogs
        if 'conditional_items' in manifest:
            mappedConditionalItems = []
            for cond in manifest['conditional_items']:
                mappedConditionalItems += [self.recursivelyMapCatalogs(request=request, manifest=cond)]
            manifest['conditional_items'] = mappedConditionalItems
        return manifest



    def get(self, request, *args, **kw):
        manifestBody = Manifest.read(kw['manifest_name'])
        manifestBody['url'] = reverse('manifest-detail', args=[kw['manifest_name']], request=request)

        # convert the catalogs to URLs
        manifestBody = self.recursivelyMapIncludedManifests(request=request, manifest=manifestBody)

        # convert the included manifests to URLS
        manifestBody = self.recursivelyMapCatalogs(request=request, manifest=manifestBody)
        return Response(manifestBody)
    

    def put(self, request, *args, **kw):
        if not request.user.has_perm('reports.change_machine'):
            return Response({
                'detail': 'Action denied for current user.'
            }, status.HTTP_403_FORBIDDEN)
        manifestName = kw['manifest_name']
        newManifest = Manifest.new()
        jsonObj = getJSONFromRequest(request)
        jsonObj.pop('url')
        newManifest.update(jsonObj)
        Manifest.write(manifestName, newManifest, request.user)
        return Response(None, status.HTTP_202_ACCEPTED)


    def delete(self, request, *args, **kw):
        if not request.user.has_perm('reports.delete_machine'):
            return Response({
                'detail': 'Action denied for current user.'
            }, status.HTTP_403_FORBIDDEN)
        manifestName = kw['manifest_name']
        Manifest.delete(manifestName, request.user)
        return Response(None, status.HTTP_204_NO_CONTENT)



class ContentTypeList(generics.ListAPIView):
    """
    API endpoint that represents a list of contenttypes.
    """
    model = ContentType
    serializer_class = ContentTypeSerializer



class ContentTypeDetail(generics.RetrieveAPIView):
    """
    API endpoint that represents a single contenttype.
    """
    model = ContentType
    serializer_class = ContentTypeSerializer



class PermissionList(generics.ListAPIView):
    """
    API endpoint that represents a list of permissions.
    """
    model = Permission
    serializer_class = PermissionSerializer



class PermissionDetail(generics.RetrieveAPIView):
    """
    API endpoint that represents a single permission.
    """
    model = Permission
    serializer_class = PermissionSerializer
    


class UserList(generics.ListCreateAPIView):
    """
    API endpoint that represents a list of users.
    """
    model = User
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    """
    API endpoint that represents a single user.
    """
    model = User
    serializer_class = UserSerializer



class GroupList(generics.ListCreateAPIView):
    """
    API endpoint that represents a list of groups.
    """
    model = Group
    serializer_class = GroupSerializer



class GroupDetail(generics.RetrieveUpdateDestroyAPIView):
    """
    API endpoint that represents a single group
    """
    model = Group
    serializer_class = GroupSerializer



class MachineList(generics.ListCreateAPIView):
    """
    API endpoint that represents a list of machines.
    """
    model = Machine
    serializer_class = MachineSerializer



class MachineDetail(generics.RetrieveUpdateDestroyAPIView):
    """
    API endpoint that represents a single machine.
    """
    model = Machine
    serializer_class = MachineSerializer



class ReportList(generics.ListAPIView):
    """
    API endpoint that represents a list of reports.
    """
    model = MunkiReport
    serializer_class = ReportSerializer



class ReportDetail(generics.RetrieveAPIView):
    """
    API endpoint that represents a single report.
    """
    model = MunkiReport
    serializer_class = ReportSerializer



class InventoryItemList(generics.ListAPIView):
    """
    API endpoint that represents a list of inventory items.
    """
    model = InventoryItem
    serializer_class = InventoryItemSerializer



class InventoryItemDetail(generics.RetrieveDestroyAPIView):
    """
    API endpoint that represents a single inventory item.
    """
    model = InventoryItem
    serializer_class = InventoryItemSerializer
